/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package codegen;

/**
 *
 * @author Felipe Gutierrez
 */


import java.util.ArrayList;
import java.util.Iterator;
import java.util.Stack;


import xmibase.XmiAbstraction;
import xmibase.XmiAssociation;
import xmibase.XmiAssociationEnd;
import xmibase.XmiAttribute;
import xmibase.XmiClass;
import xmibase.XmiDataType;
import xmibase.XmiGeneralization;
import xmibase.XmiInterface;
import xmibase.XmiPackage;
import xmibase.XmiTagDefinition;
import xmibase.XmiTagValue;
/**
 * This class organizes the information received from XMIParse in a XMIPackage.
 * The result is a XMIPackage called Application. the Application has the application name and
 * all classes. Each Class has the Primitive Attributes, Associate Attributes, TagValues, Interfaces, Generalization and
 * its normal attributes as ClassName, isAbstract and etc.
 *
 * @author Lucas
 *
 */
public class Organizer {

	private ArrayList<XmiDataType> dataTypes;
	private ArrayList<XmiClass> classes;
	private ArrayList<XmiGeneralization> generalizations;
	private ArrayList<XmiInterface> interfaces;
	private Stack<String> packageName;
	private ArrayList<XmiPackage> packages;


	private XmiPackage Application;

	public Organizer() {

		this.packageName = new Stack<String>();
		this.packages = new ArrayList<XmiPackage>();
		this.classes = new ArrayList<XmiClass>();
		this.dataTypes = new ArrayList<XmiDataType>();
		this.interfaces = new ArrayList<XmiInterface>();

	}
	/**
	 * Its executes all methods need to organize and set the information in the class
	 * @param xmiPKG
	 */
	public void execute(XmiPackage xmiPKG) {
		this.generalizations = xmiPKG.getGenerations();
		transformXMIPKGToApplication(xmiPKG);
		// set the type of the attribute at the class
		setTypeAttributes();
		// set the associate attributes
		setAssociateAttributes(xmiPKG.getAssociations());

		// set the superClass and Abstraction
		setSuperClasses();
		setAbstraction(xmiPKG.getAbstractions());
		// set the tagValues
		setNameTagValue(xmiPKG.getTagDefenition());
		setNameTagValueAssociations(xmiPKG.getTagDefenition(), xmiPKG.getAssociations());



		setApplication(xmiPKG);
		//setIsBO();
	}

	private void setIsBO() {

		for (XmiClass classe : Application.getClasses()){
			for (XmiAttribute attribute : classe.getAttributes()){
				String[] auxPackages = attribute.getPackageType().split("\\p{Punct}");

				if ( auxPackages[1].equals(Application.getName())){
					attribute.setBO(true);
				}else {
					attribute.setBO(false);
				}
			}
			for (XmiAttribute attribute : classe.getAssociatesAttributes()){
				String[] auxPackages = attribute.getPackageType().split("\\p{Punct}");

				if ( auxPackages[1].equals(Application.getName())){
					attribute.setBO(true);
				}else {
					attribute.setBO(false);
				}
			}


		}

	}
	private void setApplication(XmiPackage xmiPKG) {
		this.Application = xmiPKG;
		this.Application.setClasses(this.classes);
		this.Application.setDataTypes(this.dataTypes);
		this.Application.setPackages(this.packages);



		for (XmiTagDefinition tagDefenition : xmiPKG.getTagDefenition()) {
			if (tagDefenition.getName().equals("appName")) {
				for (XmiTagValue tagValue : xmiPKG.getTagsValue()) {

					if (tagValue.getXmiTagDefenition().getId().equals(
							tagDefenition.getId())) {
						this.Application.setName(tagValue.getTagValue());
					}
				}

			}
		}

			if( Application.getName() == null ){

				this.Application.setName("default");
			}


	}

	private void setAbstraction(ArrayList<XmiAbstraction> abstractions) {
		for (XmiClass xmiClass : this.classes) {
			if (xmiClass.hasInterface()) {
				for ( XmiAbstraction abstractionCLASS : xmiClass.getAbstractions() ) {
					for (XmiAbstraction abstractionPCK : abstractions) {

						if (abstractionCLASS.getId().equals(
								abstractionPCK.getId())) {

							for (XmiInterface xmiInterface : this.interfaces) {
								if (xmiInterface.getId().equals(
										abstractionPCK.getXmiInterface().getId())) {

									xmiClass.addInterface(xmiInterface);
									break;
								}
							}

							abstractions.remove(abstractionPCK);
							break;
						}
					}
				}
			}

		}

	}

	private void setNameTagValueAssociations(
			ArrayList<XmiTagDefinition> tagsDefenition,
			ArrayList<XmiAssociation> associations) {

		for (XmiAssociation tmpAssociation : associations) {

			for (XmiTagDefinition tagDefenition : tagsDefenition) {

				for (XmiTagValue tagValue : tmpAssociation.getTagsValue()) {

					if (tagValue.getXmiTagDefenition().getId().equals(
							tagDefenition.getId())) {
						tagValue.setName(tagDefenition.getName());
					}
				}
			}
		}

	}

	/**
	 * Set the name of TagValue at XMIClass and  XMIAttribute. for each TagDefenition,
	 * the method see each Class, if each Class has a TagValue with the same id of the TagDefenition
	 * sets the TagValue name with the same of the TagDefenition. After, it sets the name of the TagValues
	 *  in the Attribute Class,
	 *
	 * @param tagsDefenition
	 */
	private void setNameTagValue(ArrayList<XmiTagDefinition> tagsDefenition) {

		for (XmiTagDefinition tagDefenition : tagsDefenition) {
			for (XmiClass classe : this.classes) {
				for (XmiTagValue tagValue : classe.getTagsValue()) {

					if (tagValue.getXmiTagDefenition().getId().equals(
							tagDefenition.getId())) {
						tagValue.setName(tagDefenition.getName());
					}
				}
				for (XmiAttribute att : classe.getAttributes()) {
					for (XmiTagValue tagValue : att.getTagsValue()) {

						if (tagValue.getXmiTagDefenition().getId().equals(
								tagDefenition.getId())) {
							tagValue.setName(tagDefenition.getName());
						}
					}
				}

			}
		}

	}
/**
 * It sets the Associate attributes at the class. for each Class,
 * it seeks an association of this class looking at a child AssociationEND,
 * and set the aggregation of the attribute. if it has a child AssociationEND,
 * it seeks the another AssociationEND and set the others attributes of the Attribute
 *
 * @param xmiAssociation
 */
	private void setAssociateAttributes(
			ArrayList<XmiAssociation> xmiAssociation) {
		for (XmiClass tempXMIClass : this.classes) {

			//find the association of this class
			for (XmiAssociation association : xmiAssociation) {
				boolean myClassAssociation = false;
				String aggregation = "";
				// find the child associationEND
				for (XmiAssociationEnd associationEND : association
						.getAssociationsEnd()) {

					if (!(associationEND.isNavegable())) {

						if (associationEND.getXmiClass().getId().equals(
								tempXMIClass.getId())) {
							myClassAssociation = true;
							aggregation = associationEND.getAggregation();
							break;
						}
					}

				}
				// if has a association of this class
				if (myClassAssociation) {
					XmiAttribute xmiAtt = new XmiAttribute();
					// find the parent associationEnd
					for (XmiAssociationEnd associationEND : association
							.getAssociationsEnd()) {

						if ((associationEND.isNavegable())) {
							//set attributes of the Attribute
							{
								xmiAtt.setId(associationEND.getId());

								xmiAtt.setChangeability(associationEND.getChangeability());
								xmiAtt.setVisibility(associationEND.getVisibility());
								xmiAtt.setSpecification(associationEND.isSpecification());
								xmiAtt.setTargetScope(associationEND.getTargetScope());
								xmiAtt.setAggregation(aggregation);
								// multiplicidade
								xmiAtt.setLowerMultiplicity(associationEND.getLowerMultiplicity());
								xmiAtt.setUpperMultiplicity(associationEND.getUpperMultiplicity());

								//set the TagValue of the attribute
								for (XmiTagValue tagValue : association.getTagsValue()) {
									xmiAtt.addTagValue(tagValue);
								}
							}

							// find the parent class to set the attributes as DataType,
							//PackageType and Name of the attribute
							for (XmiClass associateClass : this.classes) {

								if (associateClass.getId().equals(
										associationEND.getXmiClass().getId())) {
									xmiAtt.setDataType(associateClass.getName());
									xmiAtt.setPackageType(associateClass.getPackageName());
									// the association does not have the name
									// attribute
									if ((associationEND.getName() == "")
											|| associationEND.getName() == null) {
										// TODO lower first letter at name
										// attribute
										xmiAtt.setName(lowerFirst(associateClass.getName()));
										// the association has the name
										// attribute
									} else {
										xmiAtt.setName(lowerFirst(associationEND.getName()));

									}
									break;
								}
							}
							tempXMIClass.addAssocitesAttributes(xmiAtt);
						}
					}
				}
			}
		}
	}

	/**
	 * Set Super Class in the Class. for each Class, if the Class has a SuperClass,
	 * it seeks and set the Generalization of this Class. After, it seeks the superClass.
	 */
	private void setSuperClasses() {
		// find the generalization of the class
		for (XmiClass tmpXMIClass : this.classes) {
			if (tmpXMIClass.getHasSuperClass()) {

			for (XmiGeneralization tmpXMIGen : this.generalizations) {
					if (tmpXMIClass.getGeneralization().getId().equals(
							tmpXMIGen.getId())) {
						tmpXMIClass.setGeneralization(tmpXMIGen);


						// find the super class and set in class
						for (XmiClass xmiClass : this.classes) {
							if (tmpXMIClass.getGeneralization().getIdParent().equals(
									xmiClass.getId())) {

								tmpXMIClass.setSuperClass(xmiClass);

							}
						}
						this.generalizations.remove(tmpXMIGen);
						break;
					}
				}
			}
		}
		// find the super class and set in class
		for (XmiClass tmpXMIClass : this.classes) {

			if (tmpXMIClass.getHasSuperClass()) {


			}
		}

	}
	/**
	 * It is a recursive method, first it builds the PackageName for each Class and set in it, get all Class and add in the
	 * ArrayList classes, the same to DataType and Interfaces. After, for each Package it calls itself.
	 * @param xmiParsePackage
	 */
	public void transformXMIPKGToApplication(XmiPackage xmiParsePackage) {

		this.packageName.push(xmiParsePackage.getName());
		String pkgName = getPackage();
		System.out.println("pkgName " + pkgName);
		System.out.println("pkg " + xmiParsePackage.getName() + "\n");
		for (XmiClass tmpXMIClass : xmiParsePackage.getClasses()) {

			tmpXMIClass.setPackageName(pkgName);

			if (tmpXMIClass.getPackageName() == null) {
				System.out.println("WARNNING = Class " + tmpXMIClass.getName()
						+ " with empty package");
				tmpXMIClass.setPackageName("default");

			}
			// converteXMIAttributeToAttribute(tmpXMIClass, tmpXMIClass);

			this.classes.add(tmpXMIClass);

			// this.greenBoxClasses.add(tmpXMIClass);
		}
		for (XmiDataType tmpXMIDataType : xmiParsePackage.getDataTypes()) {

			tmpXMIDataType.setPackageName(pkgName);

			this.dataTypes.add(tmpXMIDataType);
		}
		for (XmiInterface tmpXMIInterface : xmiParsePackage.getInterfaces()) {
			this.interfaces.add(tmpXMIInterface);

		}
		for (XmiPackage tmpXMIPackage : xmiParsePackage.getPackages()) {

			transformXMIPKGToApplication(tmpXMIPackage);


		}
		xmiParsePackage.setName(pkgName);
		this.packages.add(xmiParsePackage);
		this.packageName.pop();
	}
	/**
	 * It sets the type attribute of the class
	 */
	private void setTypeAttributes() {
		for (XmiClass tmpXMIClass : this.classes) {
			for (XmiAttribute tmpXMIAtt : tmpXMIClass.getAttributes()) {

				for (XmiDataType dataType : this.dataTypes) {

					if (tmpXMIAtt.getIdDataType() != null) {
						if (tmpXMIAtt.getIdDataType().equals(dataType.getId())) {
							tmpXMIAtt.setDataType(dataType.getName());
							tmpXMIAtt.setPackageType(dataType.getPackageName());
							break;
						}
					}
				}
				for (XmiClass tmpClass : this.classes) {

					if (tmpXMIAtt.getIdDataType() != null) {
						if (tmpXMIAtt.getIdDataType().equals(tmpClass.getId())) {
							tmpXMIAtt.setDataType(tmpClass.getName());
							tmpXMIAtt.setPackageType(tmpClass.getPackageName());
							break;
						}
					}

				}

			}
		}

	}
	/**
	 * Build the packageName of the current iteration of the recursive method
	 * transformXMIPKGToApplication
	 * @return
	 */
	private String getPackage() {
		String pkgName = "";
		if (!(this.packageName.empty())) {
			Iterator iterator = this.packageName.iterator();

			while (iterator.hasNext()) {
				String aux = (String) iterator.next();
				if (aux != null) {
					pkgName += aux + ".";
				}
			}
			if (pkgName != "") {
				pkgName = pkgName.substring(0, pkgName.length() - 1);
			}
		}
		return pkgName;

	}

	public ArrayList<XmiDataType> getDataTypes() {
		return dataTypes;
	}

	public void setDataTypes(ArrayList<XmiDataType> dataTypes) {
		this.dataTypes = dataTypes;
	}

	public ArrayList<XmiClass> getClasses() {
		return classes;
	}

	public void setClasses(ArrayList<XmiClass> classes) {
		this.classes = classes;
	}

	public Stack<String> getPackageName() {
		return packageName;
	}

	public void setPackageName(Stack<String> packageName) {
		this.packageName = packageName;
	}

	public ArrayList<XmiGeneralization> getGeneralizations() {
		return generalizations;
	}

	public void setGeneralizations(ArrayList<XmiGeneralization> generalizations) {
		this.generalizations = generalizations;
	}

	public String lowerFirst(String aux) {

		StringBuilder strBuilder = new StringBuilder(aux);

		strBuilder.setCharAt(0, Character.toLowerCase(strBuilder.charAt(0)));

		return strBuilder.toString();

	}

	public XmiPackage getApplicationClasses() {
		return Application;
	}

	public void setApplicationClasses(XmiPackage applicationClasses) {
		Application = applicationClasses;
	}

}
